package foundation.zk.crud.composer;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.zkoss.zhtml.Messagebox;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zul.Window;

import foundation.common.util.BusinessException;
import foundation.common.util.CommonGeneralUtil;
import foundation.common.util.CommonWebUtil;
import foundation.web.flow.AbstractNavigationComposer;
import ar.com.sne.bean.BaseBO;

public abstract class AbstractCRUDEditComposer extends AbstractNavigationComposer {

	private static final long serialVersionUID = 2199037470865728635L;
	private final Log log = LogFactory.getLog(getClass());

	public static final String OBJECT_TO_EDIT_ARGUMENT = "objectToedit";
	public static final String CALLBACK_ARGUMENT = "callback";

	// object being edited by current window (clone of edited objetct origin)
	private Object editedObject;
	
	//object origing
	private Object editedObjectOrigin;
	
	// used for i18n in Edit button click handler
	private  boolean addingNewObject;

	// used after a successful update
	public EventListener callback;

	// TODO eliminar esta referencia de ser posible
	// current ZK Edit Window
	private Window wndEdit;


	@Override
	public void doAfterCompose(Component comp) throws Exception {
		super.doAfterCompose(comp);

		addingNewObject = false;
		
		editedObject = Executions.getCurrent().getArg().get(OBJECT_TO_EDIT_ARGUMENT);
		//se clona el objeto a uno nuevo, para que si se cancela la edicion y vuelve a editar se obtengan los datos originales
//		if(editedObjectOrigin!=null){
//			editedObject =CommonGeneralUtil.clone(editedObjectOrigin);
//		}
		
		if (editedObject == null) {
			editedObject = getNewObjectToAdd();
			addingNewObject = true;
		}
		callback = (EventListener) Executions.getCurrent().getArg().get(
				CALLBACK_ARGUMENT);
	}

	/**
	 * Answer a new empty object to be added after edited by this window
	 * 
	 * @return the new object
	 */	
	protected abstract Object getNewObjectToAdd();

	/**
	 * Cancel button click handler
	 * 
	 * @param event
	 *            the triggered event
	 */
	public void onClick$cancelButton(Event event) {
		log.debug(" Cancel button click ");
		close();
	}

	/**
	 * Edit button click handler
	 * 
	 * @param event
	 *            the triggered event
	 * @throws InterruptedException
	 *             if any error occurs
	 */
	public void onClick$editButton(Event event) throws InterruptedException {

		log.debug(" Edit button click ");

		// verifyAndUpdate() could throw a BusinessException
		verifyAndUpdate();

		// verifyAndUpdate() finished ok
		String successMassage;
		String successTitle;
		if (addingNewObject) {
			successMassage = "AbstractCRUDEditComposer.creation.success";
			successTitle = "AbstractCRUDEditComposer.creation.success.title";
		} else {
			successMassage = "AbstractCRUDEditComposer.edition.success";
			successTitle = "AbstractCRUDEditComposer.edition.success.title";
		}

		Messagebox.show(CommonWebUtil.getProperty(successMassage), CommonWebUtil
				.getProperty(successTitle), Messagebox.OK,
				Messagebox.INFORMATION, callback);

		close();
	}

	/**
	 * Triggers a verification and then try to save de object calling
	 * updateObject(). if verify fails update is aborted
	 * 
	 * @throws BusinessException
	 *             if verify fails
	 */
	private void verifyAndUpdate() {
		BusinessException be = new BusinessException();
		log.debug("verifiyng before update");

		verify(be);
		CommonGeneralUtil.throwBussinesException(be);
		log.debug("executing updateObject");
		updateObject(getEditedObject());
	}

	/**
	 * Performs the object update against the service layer
	 * 
	 * @param objectToUpdate
	 *            object to be updated by the service layer
	 */
	protected abstract void updateObject(Object objectToUpdate);

	/**
	 * This method is call before executing update in order to check if the
	 * inputs fields content is ok. This check can raise one or several errors
	 * adding those to the parameter BusinessException
	 * 
	 * @param be
	 *            BusinessException to collect the errors found in check
	 */
	protected abstract void verify(BusinessException be);

	/**
	 * Answer the object being edited by this window
	 * 
	 * @return the edited object
	 */
	public Object getEditedObject() {
		return editedObject;
	}

	/**
	 * @return the addingNewObject
	 */
	public boolean isAddingNewObject() {
		return addingNewObject;
	}


	/**
	 * Close current window
	 */
	private void close() {
		// TODO eliminar esta referencia de ser posible
		Window window = (Window)this.self;
		window.detach();
	}

	public void setAddingNewObject(boolean addingNewObject) {
		this.addingNewObject = addingNewObject;
	}

	public void setEditedObject(Object editedObject) {
		this.editedObject = editedObject;
	}
	
	
	@Override
	protected void callMethods() {
		
	}
	
	@Override
	public BaseBO getNewObjectPersistentObjectToAdd(){
		if(getNewObjectToAdd()!=null && getNewObjectToAdd() instanceof BaseBO){
			return (BaseBO)getNewObjectToAdd();
		}
		else{
			return null;
		}
	}
	
}
